package com.dengbw.systemtransfer.interfaces;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider;
import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
import org.mybatis.dynamic.sql.util.SqlProviderAdapter;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@SuppressWarnings("unused")
public interface MyBaseMapper<T> extends BaseMapper<T> {

    
    @SelectProvider(type = SqlProviderAdapter.class, method = "select")
    List<T> dynamicQueryList(SelectStatementProvider selectStatement);

    
    @SelectProvider(type = SqlProviderAdapter.class, method = "select")
    T dynamicQueryOne(SelectStatementProvider selectStatement);

    
    @InsertProvider(type = SqlProviderAdapter.class, method = "insert")
    int dynamicInsert(InsertStatementProvider<T> insertStatement);

    
    @UpdateProvider(type = SqlProviderAdapter.class, method = "update")
    int dynamicUpdate(UpdateStatementProvider updateStatement);

    
    @DeleteProvider(type = SqlProviderAdapter.class, method = "delete")
    int dynamicDelete(DeleteStatementProvider deleteStatement);

    
    @SelectProvider(type = SqlProviderAdapter.class, method = "select")
    long dynamicCount(SelectStatementProvider selectStatement);

    
    default QueryWrapper<T> getQueryWrapper() {
        return new QueryWrapper<>();
    }

    
    private UpdateWrapper<T> getUpdateWrapper() {
        return new UpdateWrapper<>();
    }

    
    default List<T> queryList(Long limit, MyAction<QueryWrapper<T>> action) {
        QueryWrapper<T> qw = getQueryWrapper();
        if (action != null) {
            action.apply(qw);
        }
        return limit == null ? this.selectList(qw) : this.queryPage(limit, action).getRecords();
    }

    
    default List<T> queryList(MyAction<QueryWrapper<T>> action) {
        return this.queryList(null, action);
    }

    
    default List<T> queryList() {
        return this.queryList(null, null);
    }

    
    default IPage<T> queryPage(Long offset, Long limit, MyAction<QueryWrapper<T>> action) {
        QueryWrapper<T> qw = getQueryWrapper();
        if (action != null) {
            action.apply(qw);
        }
        Page<T> page = setPage(offset, limit);
        IPage<T> result = this.selectPage(page, qw);
        cutResult(result, offset, limit);
        return result;
    }

    default IPage<T> queryPage(Page<T> page, MyAction<QueryWrapper<T>> action) {
        QueryWrapper<T> qw = getQueryWrapper();
        if (action != null) {
            action.apply(qw);
        }
        return selectPage(page, qw);
    }

    default IPage<T> queryPage(long index, long size, MyAction<QueryWrapper<T>> action) {
        QueryWrapper<T> qw = getQueryWrapper();
        if (action != null) {
            action.apply(qw);
        }
        return selectPage(new Page<>(index, size), qw);
    }

    
    private Page<T> setPage(Long offset, Long limit) {
        Page<T> page;
        if (offset == null && limit != null) {
            page = new Page<>(1, limit);
            page.setOptimizeCountSql(false);
            page.setTotal(limit);
        } else if (offset == null || limit == null) {
            page = new Page<>(-1, -1);
        } else {
            page = new Page<>(offset, limit);
        }
        return page;
    }

    
    private void cutResult(IPage<T> result, Long offset, Long limit) {
        List<T> record = result.getRecords();
        if (limit != null && offset != null && record.size() > limit) {
            List<T> selected = new ArrayList<>();
            for (Long i = offset; i < offset + limit; i++) {
                selected.add(record.get(i.intValue()));
            }
            result.setRecords(selected);
            result.setTotal(record.size());
            result.setSize(limit);
        }
    }

    
    default IPage<T> queryPage(MyAction<QueryWrapper<T>> action) {
        return this.queryPage(null, null, action);
    }


    default IPage<T> queryPage(Long limit, MyAction<QueryWrapper<T>> action) {
        return this.queryPage(null, limit, action);
    }


    default IPage<T> queryPage() {
        return this.queryPage(null, null, null);
    }

    
    default T queryOne(MyAction<QueryWrapper<T>> action) {
        QueryWrapper<T> qw = getQueryWrapper();
        if (action != null) {
            action.apply(qw);
        }
        return this.selectOne(qw);
    }

    
    default boolean exists(MyAction<QueryWrapper<T>> action) {
        QueryWrapper<T> qw = getQueryWrapper();
        if (action != null) {
            action.apply(qw);
        }
        T t = this.selectOne(qw);
        return !Objects.isNull(t);
    }

    
    default int delete(MyAction<QueryWrapper<T>> action) {
        QueryWrapper<T> qw = getQueryWrapper();
        if (action != null) {
            action.apply(qw);
        }
        return this.delete(qw);
    }

    
    default int deleteByKey(Serializable key) {
        return this.deleteById(key);
    }

    
    default int update(MyAction<UpdateWrapper<T>> action) {
        UpdateWrapper<T> uw = getUpdateWrapper();
        if (null != action) {
            action.apply(uw);
        }
        return this.update(null, uw);
    }
}
